﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Castle.ActiveRecord;
using Castle.Components.Validator;
using MiragePWF.Common.Commons;
using MiragePWF.NH.Validation;
using Wolfut.MirageEE.Kernal.Audit;

namespace Wolfut.MirageEE.Kernal.Common
{
    /// <summary>
    /// Provides an optional base class for your objects which will be persisted to the database.
    /// Benefits include the addition of an ID property along with a consistent manner for comparing
    /// persistent objects.  If you're looking for a consistent means of comparing objects but don't
    /// need the ID property; consider inheriting from <see cref="DomainObject"/>, this object's base class.
    /// 
    /// Since nearly all of the persistent objects you create will have a type of int ID, this 
    /// base class leverages this assumption.  If you want a persistent object with a type other 
    /// than int, such as string, then use <see cref="PersistentObjectWithTypedId{IdT}" /> instead.
    /// </summary>
    [Serializable]
    public abstract class PersistentObject : DomainObject, IPersistentObjectWithTypedId<int>,IAuditableEx
    {



        #region IPersistentObjectWithTypedId Members
        /// <summary>
        /// Id may be of type string, int, custom type, etc.
        /// Setter is protected to allow unit tests to set this property via reflection and to allow 
        /// domain objects more flexibility in setting this for those objects with assigned Ids.
        /// It's virtual to allow NHibernate-backed objects to be lazily loaded.
        /// </summary>
        [PrimaryKey(Length = 4)]
        public virtual int Id
        {
            get
            {
                return id;   
            }
            
            protected set
            {
                id = value;
            }
        }

        private int id;

        /// <summary>
        /// Transient objects are not associated with an item already in storage.  For instance,
        /// a Customer is transient if its Id is 0.  It's virtual to allow NHibernate-backed 
        /// objects to be lazily loaded.
        /// </summary>
        public virtual bool IsTransient()
        {
            return Id == null || Id.Equals(default(int));
        }

        #endregion
        //#region INamed Members

        //[Property]
        //[DomainSignature]
        //[ValidateNonEmpty, ValidateIsUniqueInSameTransaction, ValidateLength(0, 255)]
        //public virtual string Name
        //{
        //    get;
        //    set;
        //}

        //#endregion


        #region Entity comparison support

        /// <summary>
        /// The property getter for SignatureProperties should ONLY compare the properties which make up 
        /// the "domain signature" of the object.
        /// 
        /// If you choose NOT to override this method (which will be the most common scenario), 
        /// then you should decorate the appropriate property(s) with [DomainSignature] and they 
        /// will be compared automatically.  This is the preferred method of managing the domain
        /// signature of entity objects.
        /// </summary>
        /// <remarks>
        /// This ensures that the entity has at least one property decorated with the 
        /// [DomainSignature] attribute.
        /// </remarks>
        protected override IEnumerable<PropertyInfo> GetTypeSpecificSignatureProperties()
        {
            IList<PropertyInfo> domainSignatureProperties = new List<PropertyInfo>();
            PropertyInfo[] propertyInfos = GetType().GetProperties();
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.IsDefined(typeof(DomainSignatureAttribute), true))
                {
                    domainSignatureProperties.Add(propertyInfo);
                }
            }
            return domainSignatureProperties;
        }

        public override bool Equals(object obj)
        {
            PersistentObject compareTo = obj as PersistentObject;

            if (ReferenceEquals(this, compareTo))
                return true;

            if (compareTo == null || !GetType().Equals(compareTo.GetType()))
                return false;

            if (HasSameNonDefaultIdAs(compareTo))
                return true;

            // Since the Ids aren't the same, both of them must be transient to 
            // compare domain signatures; because if one is transient and the 
            // other is a persisted entity, then they cannot be the same object.
            return IsTransient() && compareTo.IsTransient() &&
                HasSameObjectSignatureAs(compareTo);
        }

        /// <summary>
        /// Simply here to keep the compiler from complaining.
        /// </summary>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Returns true if self and the provided entity have the same Id values 
        /// and the Ids are not of the default Id value
        /// </summary>
        private bool HasSameNonDefaultIdAs(PersistentObject compareTo)
        {
            return !IsTransient() &&
                  !compareTo.IsTransient() &&
                  Id.Equals(compareTo.Id);
        }

        #endregion

        #region IAuditableEx Members

        private string createdBy;
        [Property("CREATED_BY")]
        public virtual string CreatedBy
        {
            get
            {
                return createdBy;
            }
            set
            {
                createdBy = value;
            }
        }
        [Property("LAST_UPDATED_BY")]
        public virtual string LastUpdatedBy
        {
            get
            {
                return lastUpdatedBy;
            }
            set
            {
                lastUpdatedBy = value;
            }
        }

        private string lastUpdatedBy;

        [Property("CREATED_DATE")]
        public virtual DateTime? CreatedDate
        {
            get
            {
                return createdDate;
            }
            set
            {
                createdDate = value;
            }
        }

        private DateTime? createdDate;


        [Version("VERSION_NO", UnsavedValue = "0")]
        public virtual long VersionNO
        {
            get
            {
                return versionNO;
            }
            set
            {
                versionNO = value;
            }
        }

        private long versionNO;
        [Property("LAST_UPDATED_DATE")]
        public virtual DateTime? LastUpdatedDate
        {
            get
            {
                return lastUpdatedDate;
            }
            set
            {
                lastUpdatedDate = value;
            }
        }
        private DateTime? lastUpdatedDate;
        #endregion
        #region "Application Context"
        public virtual IApplicationContext ApplicationContext
        {
            get { return ApplicationContextFactory.GetApplicationContext(); }
        }
        #endregion
    }

    [Serializable]
    public abstract class PersistentObjectWithGuid : DomainObject, IPersistentObjectWithTypedId<string>,IAuditableEx
    {
        #region IPersistentObjectWithTypedId Members
        /// <summary>
        /// ID may be of type string, int, custom type, etc.
        /// Setter is protected to allow unit tests to set this property via reflection and to allow 
        /// domain objects more flexibility in setting this for those objects with assigned IDs.
        /// It's virtual to allow NHibernate-backed objects to be lazily loaded.
        /// </summary>
        [PrimaryKey("ID",Generator = PrimaryKeyType.UuidHex, Params = "format=N,seperator=")]
        public virtual string Id 
        {
            get
            {
                return id;
            }
            protected set
            {
                id = value;
            }
        }

        private string id;

        /// <summary>
        /// Transient objects are not associated with an item already in storage.  For instance,
        /// a Customer is transient if its Id is 0.  It's virtual to allow NHibernate-backed 
        /// objects to be lazily loaded.
        /// </summary>
        public virtual bool IsTransient()
        {
            return Id == null || Id.Equals(default(int));
        }
        #endregion
        //#region INamed Members

        //[Property]
        //[DomainSignature]
        //[ValidateNonEmpty, ValidateIsUniqueInSameTransactionWithStringAttribute, ValidateLength(0, 255)]
        //public virtual string Name
        //{
        //    get;
        //    set;
        //}

        //#endregion

        #region Entity comparison support

        /// <summary>
        /// The property getter for SignatureProperties should ONLY compare the properties which make up 
        /// the "domain signature" of the object.
        /// 
        /// If you choose NOT to override this method (which will be the most common scenario), 
        /// then you should decorate the appropriate property(s) with [DomainSignature] and they 
        /// will be compared automatically.  This is the preferred method of managing the domain
        /// signature of entity objects.
        /// </summary>
        /// <remarks>
        /// This ensures that the entity has at least one property decorated with the 
        /// [DomainSignature] attribute.
        /// </remarks>
        protected override IEnumerable<PropertyInfo> GetTypeSpecificSignatureProperties()
        {
            IList<PropertyInfo> domainSignatureProperties = new List<PropertyInfo>();
            PropertyInfo[] propertyInfos = GetType().GetProperties();
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.IsDefined(typeof(DomainSignatureAttribute), true))
                {
                    domainSignatureProperties.Add(propertyInfo);
                }
            }
            return domainSignatureProperties;
        }

        public override bool Equals(object obj)
        {
            PersistentObjectWithGuid compareTo = obj as PersistentObjectWithGuid;

            if (ReferenceEquals(this, compareTo))
                return true;

            if (compareTo == null || !GetType().Equals(compareTo.GetType()))
                return false;

            if (HasSameNonDefaultIdAs(compareTo))
                return true;

            // Since the Ids aren't the same, both of them must be transient to 
            // compare domain signatures; because if one is transient and the 
            // other is a persisted entity, then they cannot be the same object.
            return IsTransient() && compareTo.IsTransient() &&
                HasSameObjectSignatureAs(compareTo);
        }

        /// <summary>
        /// Simply here to keep the compiler from complaining.
        /// </summary>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Returns true if self and the provided entity have the same Id values 
        /// and the Ids are not of the default Id value
        /// </summary>
        private bool HasSameNonDefaultIdAs(PersistentObjectWithGuid compareTo)
        {
            return !IsTransient() &&
                  !compareTo.IsTransient() &&
                  Id.Equals(compareTo.Id);
        }
        #region IAuditableEx Members

        private string createdBy;
        [Property("CREATED_BY")]
        public virtual string CreatedBy
        {
            get
            {
                return createdBy;
            }
            set
            {
                createdBy = value;
            }
        }
        [Property("LAST_UPDATED_BY")]
        public virtual string LastUpdatedBy
        {
            get
            {
                return lastUpdatedBy;
            }
            set
            {
                lastUpdatedBy = value;
            }
        }

        private string lastUpdatedBy;

        [Property("CREATED_DATE")]
        public virtual DateTime? CreatedDate
        {
            get
            {
                return createdDate;
            }
            set
            {
                createdDate = value;
            }
        }

        private DateTime? createdDate;


        [Version("VERSION_NO",UnsavedValue = "0")]
        public virtual long VersionNO
        {
            get
            {
                return versionNO;
            }
            set
            {
                versionNO = value;
            }
        }

        private long versionNO;
        [Property("LAST_UPDATED_DATE")]
        public virtual DateTime? LastUpdatedDate
        {
            get
            {
                return lastUpdatedDate;
            }
            set
            {
                lastUpdatedDate = value;
            }
        }
        
        private DateTime? lastUpdatedDate;
        #endregion
        #endregion
        #region "Application Context"
        public virtual IApplicationContext ApplicationContext
        {
            get { return ApplicationContextFactory.GetApplicationContext(); }
        }
        #endregion
        #region "Check support"
        private bool check;
        public virtual bool Check
        {
            get
            {
                return check;
            }
            set
            {
                check = value;

            }
        }
        #endregion
    }
}
